var jqExt = (function ($) {
    "use strict";
    if (!$) {
        window.console.log("jqExt Error: jQuery required!");
    }

    // Cache vars
    var
        validate_data_name = "jquery-ext-validation",
        csrf_token_cache,
        base_url_cache,
        js_page_id_cache,
        autoload_container = {
            GLOBAL: []
        },
        _ = {} // public members
        ;


    /* jQuery displaying
     * Kiểm tra một phần tử có đang hiển thị không?
     ==============================================*/
    $.fn.displaying = function () {
        var state = true;
        this.each (function () {
            state = state && $(this).css("display") !== "none" &&  $(this).css("opacity") > 0;
        });

        return state;
    };

    $.fn.toggle_other = function () {
        return this.each(function () {
            var $self = $(this),
                $target = $($self.data('toggle-target')),
                prevent = $self.data("toggle-prevent");
            $self.click(function (e) {
                if (prevent) {
                    e.preventDefault();
                }

                $target.slideToggle("slow");
            });
        });
    };


    function jqObject(id) {
        if (typeof id === "string") {
            id = $("#" + id);
        }
        return id;
    }

    /**
     * thay đổi trang thái hiển thị của hai phần tử (ẩn 1 và hiện 2)
     * @param id1 phần tử 1 (jquery object hoặc id)
     * @param id2 phần từ 2
     * @param out_dur thời gian ẩn
     * @param in_dur thời gian hiện
     */
    _.swap_display =  function (id1, id2, out_dur, in_dur) {
        out_dur = out_dur || 200;
        in_dur = in_dur || 300;

        id1 = jqObject(id1);
        id2 = jqObject(id2);

        id1.animate({opacity: 0}, out_dur, function () {
            id1.slideUp(in_dur);
            id2.slideDown(in_dur).animate({opacity: 1}, in_dur);
        });
    };

    /**
     * Nhận về session token đặt trong biến window.csrf_token hoặc input #csrf_token
     * @return {*}
     */
    _.csrf_token = function () {
        if (!csrf_token_cache) {
            if (window.csrf_token) {
                csrf_token_cache = window.csrf_token;
            } else {
                csrf_token_cache = $("#csrf_token").val();
            }
        }

        return csrf_token_cache;
    };

    function make_regex_element(e, min, max) {
        if (!min && min !== 0) {
            min = '';
        }
        if (!max) {
            max = min;
        }
        return new RegExp("^" + e + "{" + min + "," + max + "}$");
    }

    _.Regex = (function () {
        return {
            EMAIL: /^(\w+\.)*\w+@(\w+\.)+\w{2,4}$/,
            PHONE: make_regex_element("\\d", 10, 12),
            NULL: /^$/,
            ANY: /^.*$/,
            word: function (min, max) {
                return make_regex_element(".", min, max);
            },
            number: function (min, max) {
                return make_regex_element("\\d", min, max);
            }
        };
    })();

    _.base_url = function () {
        if (base_url_cache === null || base_url_cache === undefined) {
            //noinspection JSUnresolvedVariable
            if (window.BASE_URL) {
                base_url_cache = window.BASE_URL;
            } else {
                var $e = $("#base-url");
                if ($e.length > 0) {
                    base_url_cache = $e.val();
                } else {
                    base_url_cache = '';
                }
            }
        }

        return base_url_cache;
    };

    _.make_url = function (url) {
        if (url.indexOf(_.base_url()) === -1 && url.indexOf('http') === -1) {
            url = _.base_url() + (/^\//.test(url) ? '' : '/') + url;
        }

        return url;
    };

    _.reload = function () {
        window.location.reload();
    };

    /**
     * Hiển thị hộp thoại thông báo sau thời gian timeout thì gọi callback
     * @param msg string(required)
     * @param timeout int|function(option)
     * @param callback function(option)
     */
    _.msg = function (msg, timeout, callback) {
        // Khi người dùng gọi chỉ với 2 đối số msg và timeout, timeout là 1 hàm thì set timeout = 0 và callback là hàm
        if (typeof timeout === "function") {
            callback = timeout;
            timeout = 0;
        }

        if ($.Dialog) {
            $.Dialog.show({
                content: msg,
                timeout: timeout,
                on_close: callback
            });
        } else {
            if (timeout) {
                setTimeout(callback, timeout);
            }
            window.console.log("MSG (Dialog): " + msg);
        }
    };

    // Hiển thị thông báo lỗi dùng $.Dialog
    // callback có thể function để gọi sau khi close dialog
    // hoặc 1 object jQuery để focus tới, hoặc id của input focus tới
    _.error = function (msg, callback) {
        if ($.Dialog) {
            $.Dialog.show({
                content: msg,
                type: 'error',
                title: 'Xảy ra lỗi',
                on_close: function () {
                    if (callback === undefined) {
                        return;
                    }

                    if (typeof callback === "function") {
                        callback();
                    } else if (typeof callback === "object" && callback.focus) {
                        callback.focus();
                    } else {
                        jqObject(callback).focus();
                    }
                }
            });
        }
    };

    _.setToggle = function (selector, targetData) {
        if (!targetData) {
            targetData = "toggle";
        }

        $(selector).each(function () {
            var $this = $(this),
                target = $this.data(targetData);
            $this.click(function () {
                $(target).slideToggle("slow");
                return false;
            });
        });
    };


    _.ajax = function (url, data, callback, options) {

        if (!url && !options.url) {
            throw new _.Exception("URL Error", "Ajax", url);
        }
        var ajaxHandler,
            defaults = {
                timeout: 5000,
                type: 'post',
                dataType: 'json',
                auto_close: false
            };

        defaults.data = $.extend({}, {csrf_token: _.csrf_token()}, data);
        defaults.success = callback;

        options = $.extend({}, defaults, options);

        // Nếu $.Dialog hợp lệ (có tồn tại trong ứng dụng) thì sử dụng
        if ($.Dialog) {
            $.Dialog.show({
                content: 'Vui lòng đợi giây lát',
                loading: true,
                cancel : true,
                on_cancel: function () {
                    ajaxHandler.abort();
                    ajaxHandler = null;
                }
            });
        }

        ajaxHandler = $.ajax(_.make_url(url), options)
            .done(function () {
                if ($.Dialog) {
                    if (options.auto_close) {
                        $.Dialog.close(100);
                    } else {
                        $.Dialog.hide_loading();
                    }
                }
            })
            .fail(function (jqXHR) {
                if ($.Dialog) {
                    $.Dialog.show({
                        content: 'Không thể gửi yêu cầu đến máy chủ, bạn vui lòng thử lại sau!',
                        close: true,
                        type: 'error',
                        title: 'Lỗi kết nối'
                    });
                }

                window.console.log('ERROR Request ajax: ' + jqXHR.status + ' -> ' + jqXHR.statusText +
                    ' (' + options.url + ')');
            });
    };


    _.redirect = function (url, timer) {

        var timeout,
            delegate,
            handler;


        url = _.make_url(url);

        if (typeof  timer === "number") {
            timeout = timer;
            delegate = function () {
                clearInterval(handler);
                window.location.href = url;
            };
        } else if (typeof timer === "string" || typeof timer === "object") {
            var $timer = jqObject(timer),
                track = parseInt($timer.text(), 0);
            timeout = 1;
            delegate = function() {
                if (track <= 0) {
                    clearInterval(handler);
                    window.location.href = url;
                }
                $timer.text(track--);
            };
        } else {
            window.location.href = url;
        }
        handler = setInterval(delegate, timeout * 1000);
    };

    _.Exception = function (message, type, track) {
        this.message = message;
        this.name = type + "_Exception";
        this.type = type;
        if (track && window.console) {
            window.console.log("EXCEPTION: " + type);
            window.console.log("Message: " + message);
            window.console.log(track);
        }
    };

    _.js_page_id = function () {

        if (!js_page_id_cache) {
            var elements = ['js-page-detector', 'js-page-id', 'js_page_id'];
            for (var x in elements) {
                if (jqObject(elements[x]).length > 0) {
                    js_page_id_cache = jqObject(elements[x]).val();
                    break;
                }
            }
        }

        return js_page_id_cache;
    };

    _.register_autoload = function (page_id, callback) {
        if (!autoload_container[page_id]) {
            autoload_container[page_id] = [];
        }

        var counter = autoload_container[page_id].length;
        autoload_container[page_id][++counter] = callback;
    };

    var Cache = function (max_size) {
        this.max_size = max_size || 1000;
        this.data = {};
        this.length = 0;
    };

    Cache.prototype.store = function (key, value) {
        if (value !== undefined) {
            if (this.data[key] === undefined && this.length === this.max_size) {
                throw new _.Exception("Full cache!", "Cache");
            }
            this.data[key] = value;
        }

        return this.data[key];
    };

    Cache.prototype.unset = function (key) {
        delete  this.data[key];
    };

    Cache.prototype.has = function (key) {
        return this.data[key] !== undefined;
    };

    window.global_cache = new Cache(1000);

    _.Cache = Cache;
    _.g_cache = window.global_cache;

    var confirm_link  = function (element) {
        var $e = $(element),
            target = $e.attr('href'),
            text = $e.data('confirm-text');
        $e.click(function (e) {
            e.preventDefault();
            $.Dialog.show({
                content: text,
                confirm: true,
                type: 'warning',
                on_confirm: function () {
                    _.redirect(target);
                }
            });
        });
    };

    $.fn.confirm_link = function () {
        return this.each (function () {
            confirm_link(this);
        });
    };

    _.confirm = function (msg, on_confirm_callback, fade_out) {
        if (!fade_out) {
            fade_out = 1;
        }
        $.Dialog.show({
            content: msg,
            confirm: true,
            type: "warning",
            fadeOut: fade_out, /* bổ sung fade_out để tắt nhanh */
            on_confirm : on_confirm_callback
        });
    };

    _.refresh = function () {
        _.reload();
    };


    // Khởi tạo cần thiết cho thư viện
    $(function () {
        var x;
        _.js_page_id();
        for (x in autoload_container.GLOBAL) {
            if (typeof  autoload_container.GLOBAL[x] === "function") {
                autoload_container.GLOBAL[x]();
            }
        }

        for (x in autoload_container[js_page_id_cache]) {
            if (typeof autoload_container[js_page_id_cache][x] === "function") {
                autoload_container[js_page_id_cache][x]();
            }
        }

        // Toggle
        $("[data-func=toggle]").toggle_other();

        // Confirm links
        $("[data-func=confirm]").confirm_link();

        // Go up
        $('#go-top').go_top();
    });



    return _;

}(window.jQuery));